package com.ifacebox.web.admin.master.service;

import com.ifacebox.web.admin.logger.service.TraceLoggerService;
import com.ifacebox.web.admin.master.dao.MasterQuartzDao;
import com.ifacebox.web.admin.master.model.MasterQuartz;
import com.ifacebox.web.admin.master.model.MasterQuartzQuery;
import com.ifacebox.web.common.model.UserAgent;
import org.quartz.*;
import org.quartz.listeners.SchedulerListenerSupport;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
import java.util.UUID;

/**
 * @author znn
 */
@Service
public class MasterQuartzService extends SchedulerListenerSupport {
    private Scheduler scheduler;
    @Resource
    private MasterQuartzDao masterQuartzDao;
    @Resource
    private TraceLoggerService traceLoggerService;

    public MasterQuartzService(Scheduler scheduler) throws SchedulerException {
        this.scheduler = scheduler;
        this.scheduler.getListenerManager().addSchedulerListener(this);
    }

    public List<MasterQuartz> query(MasterQuartzQuery masterQuartzQuery) {
        return masterQuartzDao.query(masterQuartzQuery);
    }

    public Long total(MasterQuartzQuery masterQuartzQuery) {
        return masterQuartzDao.total(masterQuartzQuery);
    }

    public MasterQuartz queryById(Integer id) {
        return masterQuartzDao.queryById(id);
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean save(MasterQuartz masterQuartz, UserAgent userAgent) {
        masterQuartz.setCreateUser(userAgent.getMasterUser().getUsername());
        masterQuartz.setJobKey(UUID.randomUUID().toString());
        masterQuartz.setJobStatus(Trigger.TriggerState.NONE.toString());
        if (!ObjectUtils.isEmpty(masterQuartzDao.save(masterQuartz))) {
            this.scheduleJob(masterQuartz);
            return true;
        }
        return false;
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean edit(MasterQuartz masterQuartz, UserAgent userAgent) {
        masterQuartz.setUpdateUser(userAgent.getMasterUser().getUsername());
        if (!ObjectUtils.isEmpty(masterQuartzDao.edit(masterQuartz))) {
            this.rescheduleJob(masterQuartz);
            return true;
        }
        return false;
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean delete(MasterQuartz masterQuartz) {
        if (!ObjectUtils.isEmpty(masterQuartzDao.delete(masterQuartz.getId()))) {
            this.deleteJob(masterQuartz);
            return true;
        }
        return false;
    }

    public boolean isValidClassName(String className) {
        try {
            return ClassUtils.isPresent(className, null)
                    && ClassUtils.isAssignable(Job.class, ClassUtils.forName(className, null));
        } catch (Exception e) {
            return false;
        }
    }

    public boolean isValidExpression(String cronExpression) {
        return CronExpression.isValidExpression(cronExpression);
    }

    public boolean checkExists(MasterQuartz masterQuartz) {
        TriggerKey triggerKey = TriggerKey.triggerKey(masterQuartz.getJobKey());
        JobKey jobKey = JobKey.jobKey(masterQuartz.getJobKey());
        try {
            return scheduler.checkExists(triggerKey) && scheduler.checkExists(jobKey);
        } catch (Exception e) {
            return false;
        }
    }

    public boolean resumeJob(MasterQuartz masterQuartz) {
        JobKey jobKey = JobKey.jobKey(masterQuartz.getJobKey());
        try {
            scheduler.resumeJob(jobKey);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public boolean pauseJob(MasterQuartz masterQuartz) {
        JobKey jobKey = JobKey.jobKey(masterQuartz.getJobKey());
        try {
            scheduler.pauseJob(jobKey);
            return true;
        } catch (Exception e) {
            return false;
        }
    }


    private void scheduleJob(MasterQuartz masterQuartz) {
        TriggerKey triggerKey = TriggerKey.triggerKey(masterQuartz.getJobKey());
        JobKey jobKey = JobKey.jobKey(masterQuartz.getJobKey());
        try {
            scheduler.scheduleJob(JobBuilder.newJob()
                            .withIdentity(jobKey)
                            .withDescription(masterQuartz.getJobExplain())
                            .ofType((Class<? extends Job>) Class.forName(masterQuartz.getClassName()))
                            .build()
                    , TriggerBuilder.newTrigger()
                            .withIdentity(triggerKey)
                            .withDescription(masterQuartz.getJobExplain())
                            .withSchedule(CronScheduleBuilder.cronSchedule(masterQuartz.getCronExpression()))
                            .build());
        } catch (Exception e) {
            throw new RuntimeException("添加定时任务异常！", e);
        }
    }

    private void rescheduleJob(MasterQuartz masterQuartz) {
        TriggerKey triggerKey = TriggerKey.triggerKey(masterQuartz.getJobKey());
        try {
            scheduler.rescheduleJob(triggerKey
                    , ((CronTrigger) scheduler.getTrigger(triggerKey)).getTriggerBuilder()
                            .withIdentity(triggerKey)
                            .withDescription(masterQuartz.getJobExplain())
                            .withSchedule(CronScheduleBuilder.cronSchedule(masterQuartz.getCronExpression()))
                            .build());
        } catch (Exception e) {
            throw new RuntimeException("修改定时任务异常！", e);
        }
    }

    private void deleteJob(MasterQuartz masterQuartz) {
        TriggerKey triggerKey = TriggerKey.triggerKey(masterQuartz.getJobKey());
        JobKey jobKey = JobKey.jobKey(masterQuartz.getJobKey());
        try {
            // 停止触发器
            scheduler.pauseTrigger(triggerKey);
            // 移除触发器
            scheduler.unscheduleJob(triggerKey);
            // 删除任务
            scheduler.deleteJob(jobKey);
        } catch (Exception e) {
            throw new RuntimeException("删除定时任务异常！", e);
        }
    }

    private String getJobStatus(String jobKey) {
        TriggerKey triggerKey = TriggerKey.triggerKey(jobKey);
        try {
            Trigger.TriggerState triggerState = scheduler.getTriggerState(triggerKey);
            return triggerState.toString();
        } catch (Exception e) {
            return Trigger.TriggerState.ERROR.toString();
        }
    }

    @Override
    public void jobAdded(JobDetail jobDetail) {
        UserAgent userAgent = new UserAgent(null);
        JobKey jobKey = jobDetail.getKey();
        masterQuartzDao.editJobStatusByJobKey(this.getJobStatus(jobKey.getName()), jobKey.getName());
        traceLoggerService.addTraceLogger(userAgent, "定时任务添加！【{}】", jobKey.getName());
    }

    @Override
    public void jobDeleted(JobKey jobKey) {
        UserAgent userAgent = new UserAgent(null);
        masterQuartzDao.editJobStatusByJobKey(this.getJobStatus(jobKey.getName()), jobKey.getName());
        traceLoggerService.addTraceLogger(userAgent, "定时任务删除！【{}】", jobKey.getName());
    }

    @Override
    public void jobPaused(JobKey jobKey) {
        UserAgent userAgent = new UserAgent(null);
        masterQuartzDao.editJobStatusByJobKey(this.getJobStatus(jobKey.getName()), jobKey.getName());
        traceLoggerService.addTraceLogger(userAgent, "定时任务暂停！【{}】", jobKey.getName());
    }

    @Override
    public void jobResumed(JobKey jobKey) {
        UserAgent userAgent = new UserAgent(null);
        masterQuartzDao.editJobStatusByJobKey(this.getJobStatus(jobKey.getName()), jobKey.getName());
        traceLoggerService.addTraceLogger(userAgent, "定时任务恢复！【{}】", jobKey.getName());
    }

    @Override
    public void jobScheduled(Trigger trigger) {
        UserAgent userAgent = new UserAgent(null);
        TriggerKey triggerKey = trigger.getKey();
        masterQuartzDao.editJobStatusByJobKey(this.getJobStatus(triggerKey.getName()), triggerKey.getName());
        traceLoggerService.addTraceLogger(userAgent, "定时任务执行！【{}】", triggerKey.getName());
    }

    @Override
    public void jobUnscheduled(TriggerKey triggerKey) {
        UserAgent userAgent = new UserAgent(null);
        masterQuartzDao.editJobStatusByJobKey(this.getJobStatus(triggerKey.getName()), triggerKey.getName());
        traceLoggerService.addTraceLogger(userAgent, "定时任务移除！【{}】", triggerKey.getName());
    }

    @Override
    public void schedulerError(String msg, SchedulerException cause) {
        UserAgent userAgent = new UserAgent(null);
        traceLoggerService.addTraceLogger(userAgent, "定时任务异常！【{}】\r\n{}", msg, new StringWriter() {
            {
                cause.printStackTrace(new PrintWriter(this));
            }
        }.toString());
    }

    @Override
    public void triggerFinalized(Trigger trigger) {
        UserAgent userAgent = new UserAgent(null);
        TriggerKey triggerKey = trigger.getKey();
        masterQuartzDao.editJobStatusByJobKey(this.getJobStatus(triggerKey.getName()), triggerKey.getName());
        traceLoggerService.addTraceLogger(userAgent, "触发器完成！【{}】", triggerKey.getName());
    }

    @Override
    public void triggerPaused(TriggerKey triggerKey) {
        UserAgent userAgent = new UserAgent(null);
        masterQuartzDao.editJobStatusByJobKey(this.getJobStatus(triggerKey.getName()), triggerKey.getName());
        traceLoggerService.addTraceLogger(userAgent, "触发器暂停！【{}】", triggerKey.getName());
    }

    @Override
    public void triggerResumed(TriggerKey triggerKey) {
        UserAgent userAgent = new UserAgent(null);
        masterQuartzDao.editJobStatusByJobKey(this.getJobStatus(triggerKey.getName()), triggerKey.getName());
        traceLoggerService.addTraceLogger(userAgent, "触发器恢复！【{}】", triggerKey.getName());
    }

}
